package com.ctrip.corp.obt.shard.holder;

import static com.ctrip.corp.obt.generic.constants.GenericConstants.DEFAULT;

import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import javax.sql.DataSource;

import com.ctrip.corp.obt.generic.core.context.TenantContext;
import com.ctrip.corp.obt.generic.utils.CollectionUtils;
import com.ctrip.corp.obt.shard.constants.DbType;
import com.ctrip.corp.obt.shard.core.ShardDataSource;
import com.ctrip.corp.obt.shard.domain.dbmetainfo.DatabaseMetaInfo;
import com.ctrip.corp.obt.shard.exception.ShardException;

/**
 * @author marco.zheng
 * @version 1.0
 * @date 2022/02/09
 * @desc {@link DataSource} Holder
 * @see
 * @since 1.0
 */
public class ShardSourceHolder {

    private static final Map<String, DataSource> dataSourceCache = new ConcurrentHashMap<>(1000);

    /**
     * 维护数据源与待启用插件的关系
     */
    private static final Map<String, Set<String>> sqlPluginsCache = new ConcurrentHashMap<>(100);

    /**
     * 维护信创数据源表结构信息
     */
    private static final Map<DbType, DatabaseMetaInfo> xcDatabaseMetaInfoCache = new ConcurrentHashMap<>(100);

    private ShardSourceHolder() {
        super();
    }

    public static Set<String> getCachedTenantIds() {
        Set<String> tenantIds = new HashSet<>(dataSourceCache.keySet());
        tenantIds.remove(DEFAULT);
        return tenantIds;
    }

    public static boolean contains(String tenantId) {
        return dataSourceCache.containsKey(tenantId);
    }

    public static void remove(String tenantId) {
        dataSourceCache.remove(tenantId);
        removeCachedTenantId(tenantId);
    }

    public static void removeAll() {
        dataSourceCache.clear();
        removeCachedTenantIds(dataSourceCache.keySet());
    }

    public static DataSource getDataSource(String tenantId) {
        return dataSourceCache.get(tenantId);
    }

    public static void putDataSource(String tenantId, DataSource dataSource) {
        if (dataSource == null) {
            throw new ShardException("dataSource is null!");
        } else {
            dataSourceCache.putIfAbsent(tenantId, dataSource);
            if (tenantId.equals(DEFAULT)) {
                return;
            }
            addCachedTenantId(tenantId);
        }
    }

    public static void putDataSource(String tenantId, ShardDataSource shardDataSource) {

        Map<String, List<DataSource>> resultMap = shardDataSource.buildDataSourceCache();

        if (CollectionUtils.isEmpty(resultMap)) {
            throw new ShardException("dataSource is null!");
        } else {
            sqlPluginsCache.putAll(shardDataSource.buildSqlPlugins());
            dataSourceCache.putIfAbsent(tenantId, resultMap.get(tenantId).get(0));
            if (tenantId.equals(DEFAULT)) {
                return;
            }
            addCachedTenantId(tenantId);
        }
    }

    public static void putXcDatabaseMetaInfoCache(DbType databaseType, DatabaseMetaInfo databaseMetaInfo) {

        if (Objects.isNull(databaseMetaInfo)) {
            return;
        }
        xcDatabaseMetaInfoCache.putIfAbsent(databaseType, databaseMetaInfo);
    }

    public static Map<Object, Object> getDataSourceCache() {
        return Collections.unmodifiableMap(dataSourceCache);
    }

    public static Map<String, Set<String>> getSqlPluginsCache() {
        return Collections.unmodifiableMap(sqlPluginsCache);
    }

    public static Map<DbType, DatabaseMetaInfo> getXcDatabaseMetaInfoCache() {
        return Collections.unmodifiableMap(xcDatabaseMetaInfoCache);
    }

    public static void refreshSqlPluginCache(String tenantId, Set<String> plugins) {
        // 覆盖原plugin配置
        sqlPluginsCache.put(tenantId, plugins);
    }

    public static Set<String> getDataSourceMembers() {
        return dataSourceCache.keySet();
    }

    public static DataSource getDefaultDataSource() {
        DataSource dataSources = dataSourceCache.get(DEFAULT);
        if (dataSources == null) {
            throw new ShardException("not found default data source! please add then restart");
        }
        return dataSources;
    }

    private static void addCachedTenantId(String tenantId) {
        TenantContext.addTenantId(tenantId);
        // EnvironmentHolder.getBean(EventCenter.class).post(new TenantEvent(0, tenantId));
    }

    private static void removeCachedTenantId(String tenantId) {
        TenantContext.removeTenantId(tenantId);
        // EnvironmentHolder.getBean(EventCenter.class).post(new TenantEvent(1, tenantId));
    }

    private static void removeCachedTenantIds(Collection<String> tenantIds) {
        if (tenantIds.isEmpty()) {
            return;
        }
        tenantIds.forEach(TenantContext::removeTenantId);
        // EnvironmentHolder.getBean(EventCenter.class).post(new TenantEvent(1, tenantIds.toArray(new String[]{})));
    }

}
